/* 
 *	Copyright (C) 2007 cooleyes
 *	eyes.cooleyes@gmail.com 
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
 
#include "wavpack_parser.h"
#include "common/mem64.h"
#include <wputils.h>
#include <string.h>
#include <pspsdk.h>
#include <pspkernel.h>

#define SIRENS2_WAVPACK_SAMPLES_PER_FRAME 2048

static int32_t wpcb_read_bytes(void *id, void *data, int32_t bcount) {
	return buffered_reader_read((buffered_reader_t*)id, data, bcount);
}

static uint32_t wpcb_get_pos(void *id) {
	return buffered_reader_position((buffered_reader_t*)id);
}

static int wpcb_set_pos_abs(void *id, uint32_t pos) {
	buffered_reader_seek((buffered_reader_t*)id, pos);
	return 0;
}

static int wpcb_set_pos_rel(void *id, int32_t delta, int mode) {
	if( mode == PSP_SEEK_SET )
		buffered_reader_seek((buffered_reader_t*)id, delta);
	else if ( mode == PSP_SEEK_CUR )
		buffered_reader_seek((buffered_reader_t*)id, 
			delta + buffered_reader_position((buffered_reader_t*)id) );
	else 
		buffered_reader_seek((buffered_reader_t*)id,
			buffered_reader_length((buffered_reader_t*)id) + delta );
	return 0;
}

static int wpcb_push_back_byte(void *id, int c) {
	buffered_reader_seek((buffered_reader_t*)id, 
		buffered_reader_position((buffered_reader_t*)id) - 1);
	return(c);
}

static uint32_t wpcb_get_length(void *id) {
	return(buffered_reader_length((buffered_reader_t*)id));
}

static int wpcb_can_seek(void *id) {
	if ( !id )
		return 0;
	else
		return 1;
}


WavpackStreamReader wavpack_stream_reader = {
	wpcb_read_bytes,
	wpcb_get_pos,
	wpcb_set_pos_abs,
	wpcb_set_pos_rel,
	wpcb_push_back_byte,
	wpcb_get_length,
	wpcb_can_seek
};

WavpackParser::WavpackParser() {
	initialize();
};

WavpackParser::~WavpackParser() {
	close();
};

void WavpackParser::initialize() {
	is_open = false;
	
	total_frames = 0;
	total_samples = 0;
	
	reader = 0;
	wvc_reader = 0;
	wavpack_context = 0;
	output_buffer = 0;
	
	memset(&init_data, 0, sizeof(AudioCodecInitData));
};

void WavpackParser::finalize() {
	if ( wavpack_context ) {
		wavpack_context = WavpackCloseFile ((WavpackContext*)wavpack_context);
	}
	if ( reader ) {
		buffered_reader_close(reader);
	}
	if ( wvc_reader ) {
		buffered_reader_close(wvc_reader);
	}
	if ( output_buffer ) {
		free(output_buffer);
	}
	initialize();
};

char* WavpackParser::open(const char* filename) {
	
	reader = buffered_reader_open(filename, 131072, 1);
	if ( !reader ) {
		finalize();
		return "WavpackParser : open buffered_reader fail";
	}
	
	buffered_reader_seek(reader, 0);
	
	char *filename2 = (char*)malloc (strlen(filename) + 2);
	memset(filename2, 0, strlen(filename) + 2);
	strncpy (filename2, filename, strlen(filename));
	strcat (filename2, "c");
	wvc_reader = buffered_reader_open(filename2, 131072, 1);
        free(filename2);
        if ( wvc_reader )
        	buffered_reader_seek(wvc_reader, 0);
	
	char error [128];
	int config_bits = 0;	
	wavpack_context = WavpackOpenFileInputEx(&wavpack_stream_reader, reader, wvc_reader,
		error, (config_bits & OPEN_WVC) | OPEN_TAGS | OPEN_2CH_MAX | OPEN_NORMALIZE, 23);
	if( !wavpack_context ) {
		finalize();
		return "WavpackParser : Open Wavpack Context fail";
	}
	
	if ( WavpackGetBitsPerSample((WavpackContext*)wavpack_context) != 16 ) {
		finalize();
		return "WavpackParser : sample_bits != 16";
	}
	
	init_data.channels = WavpackGetNumChannels((WavpackContext*)wavpack_context);
	init_data.samplerate = WavpackGetSampleRate((WavpackContext*)wavpack_context);
	init_data.sample_bits = 16;
	init_data.samples_per_frame = SIRENS2_WAVPACK_SAMPLES_PER_FRAME;
	init_data.samples_per_decoded = init_data.samples_per_frame;
	init_data.frame_align = init_data.samples_per_frame * (16 >> 3) * 2;
	
	if ( init_data.channels < 1 || init_data.channels > 2 || init_data.sample_bits != 16 ) {
		finalize();
		return "WavpackParser : channels != 1 && channels != 2 || sample_bits != 16";
	}
	
	total_samples = WavpackGetNumSamples((WavpackContext*)wavpack_context);
	
	output_buffer = malloc_64(init_data.frame_align);
	if ( output_buffer == 0 ) {
		finalize();
		return "WavpackParser : malloc output_buffer fail";
	}
	
	is_open = true;
	return 0;
};

void WavpackParser::close() {
	if ( is_open ) {
		finalize();
	}
};
	
uint32_t WavpackParser::get_samplerate() {
	return init_data.samplerate;
};

uint32_t WavpackParser::get_channels() {
	return init_data.channels;
};

uint64_t WavpackParser::get_duration() {
	uint64_t duration = 1000000LL*total_samples/init_data.samplerate;
	return duration;
};
	
uint32_t WavpackParser::get_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint32_t WavpackParser::get_max_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint64_t WavpackParser::seek_time(uint64_t time) {
	uint32_t samples = (uint32_t)(time*init_data.samplerate/1000000LL);
	if ( samples > total_samples )
		samples = total_samples;
	
	WavpackSeekSample((WavpackContext*)wavpack_context, samples);
	
	uint64_t current = 1000000LL*samples/init_data.samplerate;
	return current;
};

char* WavpackParser::get_frame(void** pcm, uint32_t* samples) {
	
	if ( !is_open )
		return "WavpackParser : parser is not open";

	int actread = 0;
	memset(output_buffer, 0, init_data.frame_align);
	
	actread = init_data.samples_per_frame;
	
	int32_t buffer[SIRENS2_WAVPACK_SAMPLES_PER_FRAME*2];
	memset(buffer, 0, SIRENS2_WAVPACK_SAMPLES_PER_FRAME*2*sizeof(int32_t));
	
	actread = WavpackUnpackSamples((WavpackContext*)wavpack_context, buffer, actread);
	if ( actread < 0 )
		return "WavpackParser : unpack samples fail";
	unsigned char* p = (unsigned char*)output_buffer;
	for(int i=0; i<actread; i++) {
		p[4*i] = buffer[2*i] & 0xFF;
		p[4*i+1] = (buffer[2*i]>>8) & 0xFF;
		p[4*i+2] = (buffer[2*i+1]) & 0xFF;
		p[4*i+3] = (buffer[2*i+1]>>8) & 0xFF;
	}	
	*pcm = output_buffer;
	*samples = actread;
	return 0;
};





